Variables Are Not Boxes


In [2]:
a = [1, 2, 3]
b = a
a.append(4)
b


Out[2]:
[1, 2, 3, 4]

In [3]:
class Gizmo:
    def __init__(self):
        print('Gizmo id: %d' % id(self))

In [4]:
x = Gizmo()


Gizmo id: 1095113832488

In [5]:
y = Gizmo() * 10


Gizmo id: 1095113832376
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-5-0f524f080953> in <module>()
----> 1 y = Gizmo() * 10

TypeError: unsupported operand type(s) for *: 'Gizmo' and 'int'

Identity, Equality, and Aliases


In [6]:
charles = {'name': 'Charles L. Dodgson', 'born': 1832}

In [7]:
lewis = charles

In [8]:
lewis is charles


Out[8]:
True

In [9]:
id(charles), id(lewis)


Out[9]:
(1095096406280, 1095096406280)

In [10]:
lewis['balance'] = 950

In [11]:
charles


Out[11]:
{'balance': 950, 'born': 1832, 'name': 'Charles L. Dodgson'}

In [12]:
alex = {'name': 'Charles L. Dodgson', 'born': 1832, 'balance':950}

In [13]:
alex == charles


Out[13]:
True

In [15]:
alex is not charles


Out[15]:
True

The Relative Immutability of Tuples


In [16]:
t1 = (1, 2, [30, 40])
t2 = (1, 2, [30, 40])
t1 == t2


Out[16]:
True

In [17]:
id(t1[-1])


Out[17]:
1095111699976

In [18]:
t1[-1].append(99)

In [19]:
t1


Out[19]:
(1, 2, [30, 40, 99])

In [20]:
id(t1[-1])


Out[20]:
1095111699976

In [21]:
t1 == t2


Out[21]:
False

Copies Are Shallow by Default


In [22]:
l1 = [3, [55, 44], (7, 8, 9)]
l2 = list(l1)
l2


Out[22]:
[3, [55, 44], (7, 8, 9)]

In [23]:
l2 == l1


Out[23]:
True

In [24]:
l2 is l1


Out[24]:
False

In [25]:
l1 = [3, [66, 55, 44], (7, 8, 9)]
l2 = list(l1)
l1.append(100)
l1[1].remove(55)
print('l1:', l1)
print('l2:', l2)
l2[1] += [33, 22]
l2[2] += (10,11)
print('l1:', l1)
print('l2:', l2)


l1: [3, [66, 44], (7, 8, 9), 100]
l2: [3, [66, 44], (7, 8, 9)]
l1: [3, [66, 44, 33, 22], (7, 8, 9), 100]
l2: [3, [66, 44, 33, 22], (7, 8, 9, 10, 11)]

Deep and Shallow Copies of Arbitrary Objects


In [26]:
class Bus:
    
    def __init__(self, passengers=None):
        if passengers is None:
            self.passengers = []
        else:
            self.passengers = list(passengers)
            
    def pick(self, name):
        self.passengers.append(name)
        
    def drop(self, name):
        self.passengers.remove(name)

In [27]:
import copy

In [34]:
bus1 = Bus(['Alice','Bill', 'Claire', 'David'])
bus2 = copy.copy(bus1)
bus3 = copy.deepcopy(bus1)
id(bus1), id(bus2), id(bus3)


Out[34]:
(1095114522184, 1095114522128, 1095114522296)

In [35]:
bus1.drop('Bill')
bus2.passengers


Out[35]:
['Alice', 'Claire', 'David']

In [37]:
id(bus1.passengers), id(bus2.passengers), id(bus3.passengers)


Out[37]:
(1095114547784, 1095114547784, 1095114476232)

In [38]:
bus3.passengers


Out[38]:
['Alice', 'Bill', 'Claire', 'David']

In [39]:
a = [10, 20]
b = [a, 30]
a.append(b)
a


Out[39]:
[10, 20, [[...], 30]]

In [40]:
from copy import deepcopy
c = deepcopy(a)
c


Out[40]:
[10, 20, [[...], 30]]

Function Parameters as References


In [41]:
def f(a, b):
    a += b
    return a

In [42]:
x = 1
y = 2
f(x, y)


Out[42]:
3

In [43]:
x, y


Out[43]:
(1, 2)

In [44]:
x = [1, 2]
y = [3, 4]
f(x, y)


Out[44]:
[1, 2, 3, 4]

In [45]:
x, y


Out[45]:
([1, 2, 3, 4], [3, 4])

In [46]:
t = (10, 20)
u = (30, 40)
f(t, u)


Out[46]:
(10, 20, 30, 40)

In [47]:
t, u


Out[47]:
((10, 20), (30, 40))

Mutable Types as Parameter Defaults: Bad Idea


In [48]:
class HauntedBus(Bus):
    """A bus model haunted by ghost passengers"""
    
    def __init__(self, passengers=[]):
        self.passengers = passengers

In [49]:
bus1 = HauntedBus(['Alice', 'Bill'])
bus1.passengers


Out[49]:
['Alice', 'Bill']

In [50]:
bus1.pick('Charlie')
bus1.drop('Alice')
bus1.passengers


Out[50]:
['Bill', 'Charlie']

In [51]:
bus2 = HauntedBus()
bus2.pick('Carrie')
bus2.passengers


Out[51]:
['Carrie']

In [52]:
bus3 = HauntedBus()
bus3.passengers


Out[52]:
['Carrie']

In [53]:
bus3.pick('Dave')
bus2.passengers


Out[53]:
['Carrie', 'Dave']

In [54]:
bus2.passengers is bus3.passengers


Out[54]:
True

In [55]:
bus1.passengers


Out[55]:
['Bill', 'Charlie']

In [57]:
print(dir(HauntedBus.__init__))


['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']

In [58]:
HauntedBus.__init__.__defaults__


Out[58]:
(['Carrie', 'Dave'],)

In [59]:
HauntedBus.__init__.__defaults__[0] is bus2.passengers


Out[59]:
True

Defensive Programming with Mutable Parameters


In [60]:
class Twilightbus(Bus):
    """A bus model that makes passengers vanish"""
    
    def __init__(self, passengers=None):
        if passengers is None:
            self.passengers = []
        else:
            self.passengers = passengers

In [61]:
basketball_team = ['Sue', 'Tina', 'Maya', 'Diana', 'Pat']
bus = Twilightbus(basketball_team)
bus.drop('Tina')
bus.drop('Pat')
basketball_team


Out[61]:
['Sue', 'Maya', 'Diana']

del and Garbage Collection


In [ ]: